module EEPROM (address, data);

input [7:0] address;
output [7:0] data;

reg [7:0] data;

parameter RA = 2'b00;
parameter RB = 2'b01;
parameter RC = 2'b10;
parameter RD = 2'b11;

parameter MOV = 4'b0011;
parameter INC = 4'b0101;
parameter OUT = 4'b1111;
parameter MVI = 4'b0100;
parameter AND = 4'b1000;
parameter OR  = 4'b1001;
parameter ADD = 4'b0110;
parameter SUB = 4'b0111;
parameter PUSH = 4'b1100;
parameter POP = 4'b1101;
parameter JMP = 4'b0010;
parameter SC  = 4'b1010;
parameter CC  = 4'b1011;
parameter JC  = 4'b0001;
parameter IN  = 4'b1110;
parameter NOP = 4'b0000;

parameter zero = 2'b00;

always @(address) begin
	case(address)
	
//start:  in RC         ; read input value and store in register C
0:	data = { IN, RC, zero};
//        or RC,RC      ; just to make sure you can do this :)
1:	data = { OR, RC, RC };
//        *** initialize registers
//        cc            ; clear carry
2: 	data = { CC, 4'b0000 };
//        mvi RA,0      ; clear registers RD and RB
3:  data = { MVI, zero, zero };
//        mov RA,RD     ; most significant nibble of count, count.ms = 0
4:  data = { MOV, RA, RD };
//        and RB,RA     ; should set RB to 0 since anything & 0 is 0
5:  data = { AND, RB, RA };
//        mov RC,RA     ; least significant nibble of count, count.ls = input
6:  data = { MOV, RC, RA }; 
//        *** count = input * 4
//        add RC,RA     ; RC = RC + RA (count*2)
7:	data = { ADD, RC, RA };
//        add RD,RB     ; RD = RD + 0 + carry
8:  data = { ADD, RD, RB };
//        add RC,RA     ; RC = RC + RA (count*3)
9:  data = { ADD, RC, RA };
//        add RD,RB     ; RD = RD + 0 + carry
10: data = { ADD, RD, RB };
//        add RC,RA     ; RC = RC + RA (count*4)
11: data = { ADD, RC, RA };
//        add RD,RB     ; RD = RD + 0 + carry
12: data = { ADD, RD, RB };
//        *** initial output is same as input value
//        out RA
13: data = { OUT, RA, zero };
//        *** save items on stack
//        push RC       ; count.ls
14: data = { PUSH, RC, zero };
//        push RD       ; count.ms
15: data = { PUSH, RD, zero };
//        push RA       ; last output
16: data = { PUSH, RA, zero };
//        push RA       ; initial input
17: data = { PUSH, RA, zero };
//        *** main program loop
//        *** main delay loop, increment count until overflow (carry) occurs
//main:   mvi RA,loop1.ls     ; loop1.ls address stored in RA
18: data = { MVI, 4'b0011 };
//loop1:  inc RC        ; RC = RC + 1
19: data = { INC, RC, zero };
//        add RD,RB     ; RD = 0 + carry
20: data = { ADD, RD, RB };
//        nop           ; increase delay with nop
21: data = { NOP, zero, zero };
//        jc break1     ; break from loop if carry is produced
22: data = { JC, zero, 2'b10 };
//        jmp loop1     ; most significant nibble of loop1 address is immediate field
23: data = { JMP, 4'b0001 };
//        *** check if the user changed the input value
//break1: pop RD        ; restore initial input value
24: data = { POP, RD, zero };
//        mov RD,RC     ; save initial input value in RC
25: data = { MOV, RD, RC };
//        in RA         ; read current input value
26: data = { IN, RA, zero };
//        sc            ; prepare to do equality check
27: data = { CC, zero, zero };
//        sub RD,RA     ; RD will be zero iff RA==RD
28: data = { SUB, RD, RA };
//       mvi RA,1      ; prepare to do zero check
29: data = { MVI, 4'b0001 };
//      sc
30: data = { CC, zero, zero };
//        sub RD,RA     ; carry (overflow) will be produced if NOT zero
31: begin 
			$display($time, " < Subtracting before the death jump. "); 
			data = { SUB, RD, RA };
			end
//        jc break2     ; need to recompute count value
32: data = { JC, 4'b0011 };

// If carry is 1, it's the same, so we want to execute.
// Else, break to 49
33: data = { MVI, 4'b0011};

34: data = { JMP, 4'b0001 };
//        *** compute logical inverse of last output and make it the new output
//        pop RA        ; restore last output value
35: begin $display($time, " @ Actually got past the jump of doom."); data = { POP, RA, zero }; end
//        sub RB,RA     ; RB = 0 - RA (1's complement) or RB = ~RA (Carry is clear, pop should not alter carry)
36: data = { SUB, RB, RA };
//        out RB        ; output the value
37: data = { OUT, RB, zero };
//        mov RC,RA     ; saved initial input to RA
38: data = { MOV, RC, RA };
//        pop RD        ; restore computed count value
39: data = { POP, RD, zero };
//        pop RC
40: data = { POP, RC, zero };
//        *** Restore the stack for next iteration
//        push RC       ; ls count nibble
41: data = { PUSH, RC, zero };
//        push RD       ; ms count nibble
42: data = { PUSH, RD, zero };
//        push RB       ; last output value
43: data = { PUSH, RB, zero };
//        push RA       ; initial input value
44: data = { PUSH, RA, zero };
//        *** Set RB to zero 
//        mvi RA,0 
45: data = { MVI, RA, zero };
//        mov RA,RB
46: data = { MOV, RA, RB };
//        mvi RA,main.ls ; ls nibble of main address stored in RA
47: data = { MVI, 4'b0001 };
//        jmp main      ; ms nibble of main address is immediate field
48: data = { JMP, 4'b0001 };
//        *** Restart if user has changed input value
//break2: pop RA        ; flush the stack
49: data = { POP, RA, zero };
//        pop RA
50: data = { POP, RA, zero };
//        pop RA
51: data = { POP, RA, zero };
//        mvi RA,start.ls ; ls nibble of start address stored in RA
52: data = { MVI, 4'b0000 };
//        jmp start	
53: data = { JMP, 4'b0000 };

// Subtraction test:
/*
	0: data = { MVI, 4'b0011 };
	1: data = { MOV, RA, RB  };
	2: data = { CC, zero, zero };
	3: data = { SUB, RA, RB  };
	4: data = { MOV, RA, RD  };
	5: data = { MVI, zero, 2'b01 };
	6: begin data = { SUB, RD, RA };
		$display("Done at ", $time);
		end
	
	
/*

		
		0:  data = 8'b11101000;//	[E8]
		1:  data = 8'b10011010;//	[9A]
		2:  data = 8'b10110000;//	[B0]
		3:	data = 8'b01000000;//	[40]
		4:	data = 8'b00110011;//	[33]
		5:  data = 8'b10000100;//	[84]
		6:	data = 8'b00111000;//	[38]
		7:	data = 8'b01101000;//	[68]
		8:	data = 8'b01101101;//	[6D]
		9:	data = 8'b01101000;//	[68]
		10:	data = 8'b01101101;//	[6D]
		11:	data = 8'b01101000;//	[68]
		12:	data = 8'b01101101;//	[6D]
		13:	data = 8'b11110000;//	[F0]
		14:	data = 8'b11001000;//	[C8]
		15:	data = 8'b11001100;//	[CC]
		16:	data = 8'b11000000;//	[C0]
		17:	data = 8'b11000000;//	[C0]
		18:	data = 8'b01000011;//	[43] // main: physical address:  00010010
		19:	data = 8'b01011000;//	[58] // loop1: physical address: 00010011
		20:	data = 8'b01101101;//	[6D]
		21:	data = 8'b00000000;//	[00]
		22:	data = 8'b00010010;//	[12]
		23:	data = 8'b00100001;//	[21]
		24:	data = 8'b11011100;//	[DC] breaK:
		25:	data = 8'b00111110;//	[3E]
		26:	data = 8'b11100000;//	[E0]
		27:	data = 8'b10100000;//	[A0]
		28:	data = 8'b01111100;//	[7C]
		29:	data = 8'b01000001;//	[41]
		30:	data = 8'b10100000;//	[A0]
		31:	data = 8'b01111100;//	[7C]
		32:	data = 8'b00011111;//	[1F]
		// *** compute logical inverse ** 
		33:	data = 8'b11010000;//	[D0]
		34:	data = 8'b01110100;//	[74]
		35:	data = 8'b11110100;//	[F4]
		36:	data = 8'b00111000;//	[38]
		37:	data = 8'b11011100;//	[DC]
		38:	data = 8'b11011000;//	[D8]
		// ** restore stack ** 
		39:	data = 8'b11001000;//	[C8]
		40:	data = 8'b11001100;//	[CC]
		41:	data = 8'b11000100;//	[C4]
		42:	data = 8'b11000000;//	[C0]
		// ** set rb to zero
		43:	data = 8'b01000000;//	[40]
		44:	data = 8'b00110001;//	[31]
		45:	data = 8'b01000010;//	[41]
		46:	data = 8'b00100001;//	[22]
		// ** restart if user has changed input value
		47:	data = 8'b11010000;//	[D0] break2:
		48:	data = 8'b11010000;//	[D0]
		49:	data = 8'b11010000;//	[D0]
		50:	data = 8'b01000000;//	[41]
		51:	data = 8'b00100000;//	[22]
				
		
		
		/*0:  data = { CC, 4'b0000 };
		1:	data = { JC, 4'b0011 };
		2:  data = { MVI, 4'b0000 };
		3:  data = { OUT, RA, 2'b00 };
		4:  data = { MVI, 4'b1111 };
		5:  data = { OUT, RA, 2'b00 };
		*/
		
		/*
		0: data = {IN, RA, 2'b00};
		1: data = {OUT, RA, 2'b00};
		*/
		default : data = 8'h00;
	endcase
end
endmodule
